package pl.edu.agh.anomalyids.network.sniffer;

import java.util.ArrayList;
import java.util.List;

import org.jnetpcap.Pcap;
import org.jnetpcap.PcapIf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import pl.edu.agh.anomalyids.network.Interface;
import pl.edu.agh.anomalyids.network.listener.PacketListener;
import pl.edu.agh.anomalyids.network.sniffer.exception.OpeningNetworkInterfaceException;

public class Sniffer extends Thread {
	
	private static final Logger logger = LoggerFactory.getLogger(Sniffer.class);
	
	private List<PacketListener> listeners = new ArrayList<PacketListener>();
	private String interfaceName;
	private long startTime;
	private long workTime;
	
	public Sniffer(String interfaceName, long milis) {
		this.interfaceName = interfaceName;
		this.workTime = milis;
	}
	
	public Sniffer(String interfaceName) {
		this(interfaceName, 0);
	}
	
	@Override
	public void run() {
		Pcap pcap = null;
		try {
			pcap = openNetworkInterface();
			PacketHandler packetHandler = createPacketHandler(pcap);
			
			startTime = System.currentTimeMillis();
			
			while (!isExpired() && !Thread.interrupted()) {
		        pcap.loop(1, packetHandler, null);
			}

		} catch (OpeningNetworkInterfaceException e) {
			logger.error("Cannot open network interface.", e);
		} finally {
			if (pcap != null) {
				pcap.close();
			}
		}
		super.run();
	}
	
	private boolean isExpired() {
		if (workTime > 0) {
			return startTime + workTime < System.currentTimeMillis();
		}
		
		return false;
	}
	
	private Pcap openNetworkInterface() throws OpeningNetworkInterfaceException {
	    int snaplen = 64 * 1024;
	    int flags = Pcap.MODE_PROMISCUOUS;
	    int timeout = 10;
	    StringBuilder errorBuffer = new StringBuilder();
	    
	    Pcap pcap = Pcap.openLive(interfaceName, snaplen, flags, timeout, errorBuffer);  
	      
	    if (pcap == null) {  
	    	throw new OpeningNetworkInterfaceException();
	    }
	    
	    return pcap;
	}
	
	private PacketHandler createPacketHandler(Pcap pcap) {
		return new PacketHandler(listeners);  
	}
	
	public static List<Interface> getInterfacesList() {
	    List<PcapIf> allInterfaces = new ArrayList<PcapIf>(); 
	    StringBuilder errorBuffer = new StringBuilder();
	                      
	    int errorNumber = Pcap.findAllDevs(allInterfaces, errorBuffer);  
	    if (errorNumber == Pcap.NOT_OK || allInterfaces.isEmpty()) {  
	    	logger.warn("Cannot get list of interfaces.");
	    } else {
	    	logger.info("List of interfaces retrieved successfully.");
	    }
	    
	    List<Interface> interfaces = new ArrayList<Interface>();
	    for (PcapIf pcapInterface : allInterfaces) {
	    	interfaces.add(new Interface(pcapInterface.getName(), pcapInterface.getDescription()));
	    }
	    
	    return interfaces;
	}
	
	public void addPacketListener(PacketListener packetListener) {
		synchronized(listeners) {
			listeners.add(packetListener);
		}
	}
	
	public void removePacketListener(PacketListener packetListener) {
		synchronized(listeners) {
			listeners.remove(packetListener);
		}
	}
}
